﻿new function(jQun, Class, EventTargetList, Event, getCSSNumberValue, toArray, defineProperties){

this.Canvas = (function(DEG, canvasHtml, readyEvent, forEach, round, undefined){
	function Canvas(_element, _width, _height, _style){
		///	<summary>
		///	关于画布与其相关的处理。
		///	</summary>
		///	<param name="_element" type="HTMLCanvasElement">指定现有的canvas元素，不提供则自动提供。</param>
		///	<param name="_width" type="Number">需要设定的宽度，默认300。</param>
		///	<param name="_height" type="Number">需要设定的高度，默认150。</param>
		///	<param name="_style" type="Object">需要设定的样式键值对。</param>
		var	element = _element || canvasHtml.create(null, true)[0];

		this.assign({
			element : element,
			context : element.getContext("2d")
		});

		if(
			_style
		){
			var canvas = this;

			new EventTargetList(
					[element]
				)
				.attach({
					ready : function(){
						canvas.style(_style);
					}
				});
		}

		this.resize(
			_width || this.width,
			_height || this.height
		);
	};
	Canvas = new Class(Canvas, "jQun.Canvas");

	Canvas.props({
		clear : function(_left, _top, _width, _height){
			///	<summary>
			///	清空局部画布。
			///	</summary>
			///	<param name="_left" type="Number">距离画布左上角的水平距离。</param>
			///	<param name="_top" type="Number">距离画布左上角的垂直距离。</param>
			///	<param name="_width" type="Number">局部区域的宽。</param>
			///	<param name="_height" type="Number">局部区域的高。</param>
			var context = this.context;

			context
				.clearRect
				.apply(
					context,
					arguments.length == 4 ? arguments : [0, 0, this.width, this.height]
				);

			if(
				arguments.length > 0
			){
				if(
					_width !== this.width || _height !== this.height
				){
					return this;
				}
			}

			readyEvent.trigger(this.element);
			return this;
		},
		context : null,
		draw : function(obj, _args){
			///	<summary>
			///	在画布上绘制图像。
			///	</summary>
			///	<param name="obj" type="Canvas, Image, HTMLCanvasElement">需要绘制的图像对象。</param>
			///	<param name="_args" type="Number">绘制参数。</param>
			var element = this.element, context = this.context,

				args = toArray(arguments);

			if(
				obj instanceof Canvas.constructor
			){
				args[0] = obj.element;
			}

			if(
				args.length === 1
			){
				args.push(0, 0);
			}

			context.drawImage.apply(context, args);
			return this;
		},
		element : null,
		getColor : function(){
			///	<summary>
			///	获取画笔颜色。
			///	</summary>
			return this.context.fillStyle;
		},
		gray :  function(_left, _top, _width, _height){
			///	<summary>
			///	将画布上指定区域的图像进行黑白处理。
			///	</summary>
			///	<param name="_left" type="Number">距离画布左上角的水平距离。</param>
			///	<param name="_top" type="Number">距离画布左上角的垂直距离。</param>
			///	<param name="_width" type="Number">局部区域的宽。</param>
			///	<param name="_height" type="Number">局部区域的高。</param>
			var element = this.element, context = this.context,
			
				left = _left || 0, top = _top || 0,
				
				width = _width || element.width, height = _height || element.height,

				imageData = context.getImageData(left, top, width, height), data = imageData.data;

			for(
				var i = 0,
					j = width * height;
				i < j * 4;
				i += 4
			){
				data[i] =
				data[i + 1] =
				data[i + 2] =
					round((16843 * data[i] + 33030 * data[i + 1] + 6423 * data[i + 2]) / 65536 + 16);
			}

			context.putImageData(imageData, left, top);
			return this;
		},
		isOpacity : function(left, top){
			///	<summary>
			///	判断画布指定像素点是否为完全透明。
			///	</summary>
			///	<param name="left" type="Number">距离画布左上角的水平距离。</param>
			///	<param name="top" type="Number">距离画布左上角的垂直距离。</param>
			return this.context.getImageData(round(left), round(top), 1, 1).data[3] === 0;
		},
		resize : function(width, height){
			///	<summary>
			///	为画布重新指定宽高。
			///	</summary>
			///	<param name="width" type="Number">指定的宽度。</param>
			///	<param name="height" type="Number">指定的高度。</param>
			this.width = width;
			this.height = height;

			readyEvent.trigger(this.element);
			return this;
		},
		restore : function(){
			///	<summary>
			///	恢复画布记录的画笔信息。
			///	</summary>
			this.context.restore();
			return this;
		},
		save : function(){
			///	<summary>
			///	保存画布现在的画笔信息。
			///	</summary>
			this.context.save();
			return this;
		},
		setColor : function(color){
			///	<summary>
			///	设置画笔颜色。
			///	</summary>
			///	<param name="color" type="String">指定的css颜色值。</param>
			var context = this.context;

			context.fillStyle = context.strokeStyle = color;
			return this;
		},
		setShadow : function(shadow){
			///	<summary>
			///	设置画笔阴影，格式与css一样。
			///	</summary>
			///	<param name="shadow" type="String">指定的css阴影值。</param>
			var context = this.context;

			forEach(
				shadow.match(/\S+\([\s\S]+?\)|\S+/g),
				function(value, i){
					context[this[i]] = i === 3 ? value : getCSSNumberValue(value);
				},
				["shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor"]
			);

			return this;
		},
		setTransform : function(transform){
			///	<summary>
			///	设置转换，格式与css一样。
			///	</summary>
			///	<param name="transform" type="String">指定的css转换值。</param>
			var transformObj;
			
			this.context.setTransform(1, 0, 0, 1, 0, 0);

			transformObj = {
				rotate : { x : 0, y : 0, z : 0, isRewrite : false },
				scale : { x : 1, y : 1, z : 1, isRewrite : false },
				translate : { x : 0, y : 0, z : 0, isRewrite : false }
			};

			transform.replace(
				/(\S+?)((?:[XYZ])|3d)\(([\s\S]*?)\)/ig,
				function(matched, name, dir, params){
					var property = transformObj[name];

					if(
						property
					){
						if(
							dir === "3d"
						){
							var p = params.split(",");

							property.x = getCSSNumberValue(p[0]);
							property.y = getCSSNumberValue(p[1]);
							property.z = getCSSNumberValue(p[2]) || property.z;
						}
						else {
							property[dir.toLowerCase()] = getCSSNumberValue(params);
						}

						property.isRewrite = true;
					}

					return "";
				}
			);

			forEach(
				transformObj,
				function(property, name){
					if(
						!property.isRewrite
					){
						return;
					}

					this[name].apply(this, name === "rotate" ? [property.z * DEG] : [property.x, property.y]);
				},
				this.context
			);

			return this;
		},
		setType : function(type){
			///	<summary>
			///	设置画笔风格。
			///	</summary>
			///	<param name="type" type="String">画笔风格：stroke或fill。</param>
			this.type = type;
			return this;
		},
		style : function(style){
			///	<summary>
			///	设置画笔样式，包括颜色、风格、阴影、转换等。
			///	</summary>
			///	<param name="style" type="Object">画笔样式。</param>
			var context = this.context;

			forEach(style, function(value, name){
				var handler = this["set" + name[0].toUpperCase() + name.substring(1)];

				if(typeof handler === "function"){
					handler.call(this, value);
					return;
				}

				context[name] = value;
			}, this);

			return this;
		},
		toURL : function(_mimeType, _quality){
			///	<summary>
			///	将画布内容生成base64字符串链接。
			///	</summary>
			///	<param name="_mimeType" type="String">生成的格式。</param>
			///	<param name="_quality" type="Number">生成的清晰度。</param>
			return this.element.toDataURL.apply(this.element, arguments);
		},
		type : "fill"
	});

	Canvas.props({
		height : {
			get : function(){
				return this.element.height;
			},
			set : function(height){
				this.element.height = height;
			}
		},
		width : {
			get : function(){
				return this.element.width;
			},
			set : function(width){
				this.element.width = width;
			}
		}
	}, { gettable : true, settable : true });

	return Canvas.constructor;
}(
	// DEG
	Math.PI / 180,
	// canvasHtml
	new jQun.HTML("<canvas></canvas>"),
	// readyEvent
	new Event("ready"),
	jQun.forEach,
	Math.round,
	undefined
));

this.CanvasText = (function(Canvas, localWrite){
	function CanvasText(_text, _width, _height, _startX, _startY, _style){
		///	<summary>
		///	画布文本类。
		///	</summary>
		///	<param name="_text" type="String">需要显示在画布上的文本。</param>
		///	<param name="_width" type="Number">需要设定的宽度。</param>
		///	<param name="_height" type="Number">需要设定的高度。</param>
		///	<param name="_style" type="Object">需要设定的样式键值对。</param>
		var canvasText = this, context = this.context;

		context.textBaseline = "middle";

		new EventTargetList(
				[this.element]
			)
			.attach({
				ready : function(){
					context.textBaseline = "middle";
					context.font = canvasText.font;
				}
			});

		if(
			!_text
		){
			return;
		}
		
		this.write(_text, _startX, _startY);
	};
	CanvasText = new Class(CanvasText, "jQun.CanvasText", Canvas.prototype);

	CanvasText.props({
		font : "10px sans-serif",
		letterSpacing : 0,
		lineHeight : 18,
		setLetterSpacing : function(letterSpacing){
			///	<summary>
			///	设置字间距。
			///	</summary>
			///	<param name="letterSpacing" type="Number">需要设置的间距大小。</param>
			this.letterSpacing = getCSSNumberValue(letterSpacing);
			return this;
		},
		setLineHeight : function(lineHeight){
			///	<summary>
			///	设置行高。
			///	</summary>
			///	<param name="lineHeight" type="Number">需要设置的行高。</param>
			this.lineHeight = getCSSNumberValue(lineHeight);
			return this;
		},
		setFont : function(font){
			///	<summary>
			///	设置字体。
			///	</summary>
			///	<param name="font" type="String">需要设置的字体，格式与css一样。</param>
			var result = font.match(/\b\d+\w+\b/ig);

			this.context.font = font;

			if(
				result
			){
				this.setLineHeight(result[0]);
			}

			return this;
		},
		setTextAlign : function(textAlign){
			///	<summary>
			///	设置文本对齐。
			///	</summary>
			///	<param name="textAlign" type="String">需要设置的对齐方式，值与css一样。</param>
			this.textAlign = textAlign;
			return this;
		},
		setTextShadow : function(shadow){
			///	<summary>
			///	设置文本阴影。
			///	</summary>
			///	<param name="shadow" type="String">需要设置的阴影值，格式与css一样。</param>
			return this.setShadow(shadow);
		},
		setWhiteSpace : function(whiteSpace){
			///	<summary>
			///	设置文本该如何换行。
			///	</summary>
			///	<param name="whiteSpace" type="String">如果值不为normal，则视为不换行，格式与css一样。</param>
			this.whiteSpace = whiteSpace;
			return this;
		},
		textAlign : "left",
		whiteSpace : "normal",
		write : function(text, _startX, _startY){
			localWrite(
				this,
				this.element, this.context,
				text, _startX || 0, _startY || 0,
				this.type + "Text", this.letterSpacing, this.lineHeight, this.textAlign,
				this.whiteSpace === "normal"
			);
			return this;
		}
	});

	return CanvasText.constructor;
}(
	this.Canvas,
	// localWrite
	function(canvasText, element, context, text, startX, startY, writeType, letterSpacing, lineHeight, textAlign, shouldWrap){
		var lines = shouldWrap ? text.split("\n") : [text], top = lineHeight / 2 + startY, 
		
			width = element.width, height = element.height;

		lines.every(function(line){
			var indent = 0, left = startX, chars = [], num = line.length - 1;

			toArray(line).every(function(char, i){
				var charWidth = context.measureText(char).width;

				if(left + charWidth > width){
					if(i > 0){
						left -= letterSpacing;
					}
					return false;
				}

				chars.push({ char : char, left : left });
				left += charWidth + (i === num ? 0 : letterSpacing);
				return true;
			});

			indent = textAlign === "left" ? 0 : textAlign === "right" ? width - left : (width - left) / 2;

			if(indent < 0){
				indent = 0;
			}

			chars.forEach(function(data){
				context[writeType](data.char, indent + data.left, top);
			});

			top += lineHeight;
			return height > top - (lineHeight / 2);
		});
	}
));

this.CanvasImage = (function(Canvas){
	function CanvasImage(image, _width, _height, _style){
		///	<summary>
		///	画布图像类。
		///	</summary>
		///	<param name="image" type="Image">需要绘制的图片。</param>
		///	<param name="_width" type="Number">需要设定的宽度。</param>
		///	<param name="_height" type="Number">需要设定的高度。</param>
		///	<param name="_style" type="Object">需要设定的样式键值对。</param>

		// 此类旋转之后，需要clear，再draw，很麻烦，需要优化
		var element = this.element;

		this.draw(image, 0, 0, element.width, element.height);
	};
	CanvasImage = new Class(CanvasImage, "jQun.CanvasImage", Canvas.prototype);

	return CanvasImage.constructor;
}(
	this.Canvas
));

this.CanvasRect = (function(Canvas){
	function CanvasRect(width, height, _style){
		///	<summary>
		///	画布矩形类。
		///	</summary>
		///	<param name="_width" type="Number">需要设定的宽度。</param>
		///	<param name="_height" type="Number">需要设定的高度。</param>
		///	<param name="_style" type="Object">需要设定的样式键值对。</param>
		var type = this.type, context = this.context;

		if(
			type === "fill"
		){
			context.fillRect(0, 0, width, height);
			return;
		}

		var lineWidth = context.lineWidth;

		context.strokeRect(lineWidth / 2, lineWidth / 2, width - lineWidth, height - lineWidth);
	};
	CanvasRect = new Class(CanvasRect, "jQun.CanvasRect", Canvas.prototype);

	return CanvasRect.constructor;
}(
	this.Canvas
));

this.CanvasEllipse = (function(Canvas){
	function CanvasEllipse(width, height, _style){
		///	<summary>
		///	画布椭圆类。
		///	</summary>
		///	<param name="_width" type="Number">需要设定的宽度。</param>
		///	<param name="_height" type="Number">需要设定的高度。</param>
		///	<param name="_style" type="Object">需要设定的样式键值对。</param>
		var context = this.context, rx = width / 2, ry = height / 2,

			// 控制点的X坐标，等于X轴半径除以0.75
			cx = rx / 0.75;

		context.beginPath();
		context.moveTo(rx, 0);

		/*
			椭圆的上下两个顶点的切线是与水平线平行（如果不平行，是不可能构成平滑的封闭空间），
			所以第一个控制点的Y坐标是0，第二个控制点的Y坐标是高度。
		*/
		context.bezierCurveTo(rx + cx, 0, rx + cx, height, rx, height); 
		// 由于是从下往上画，所以第一个控制点的Y坐标是高度，第二个控制点的Y坐标是0。
		context.bezierCurveTo(rx - cx, height, rx - cx, 0, rx, 0);

		context[this.type]();
		context.closePath();
	};
	CanvasEllipse = new Class(CanvasEllipse, "jQun.CanvasEllipse", Canvas.prototype);

	return CanvasEllipse.constructor;
}(
	this.Canvas
));

this.CanvasMask = (function(Canvas, CanvasEllipse, HTMLElementList, SIZE, clearEvent, abs, ceil){
	function CanvasMask(element, _image, _max){
		///	<summary>
		///	画布擦拭性遮罩。
		///	</summary>
		///	<param name="element" type="HTMLCanvasElement">指定现有的canvas元素，不提供则自动提供。</param>
		///	<param name="_image" type="HTMLImageElement">可指定的遮罩图片。</param>
		///	<param name="_max" type="Number">手指滑动的最大距离。</param>
		var canvasMask = this, lastX = 0, lastY = 0,
		
			ellipse = new CanvasEllipse(SIZE, SIZE),

			canvasList = new HTMLElementList(element);

		this.assign({
			image : _image,
			max : _max
		});

		canvasList.attach({
			touchstart : function(e){
				var touch = e.changedTouches[0], rect = canvasList.rect();
				
					// 记录X、Y
					lastX = touch.pageX - rect.left - SIZE / 2;
					lastY = touch.pageY - rect.top - SIZE / 2;
			},
			touchmove : function(e){
				var current = canvasMask.current;

				if(
					current === 0
				){
					return;
				}

				var touch = e.changedTouches[0], rect = canvasList.rect(),
				
					// 当前点X、Y
					x = touch.pageX - rect.left - SIZE / 2, y = touch.pageY - rect.top - SIZE / 2,
					
					// 差值
					diffX = x - lastX, diffY = y - lastY,
					
					// 绝对值
					absDiffX = abs(diffX), absDiffY = abs(diffY);

				for(
					var i = 0,
						// 计算绘制次数，使得绘制的更均匀
						t = ceil(
							// 按10的倍数算次数
							(
								// 算出较大的偏移量
								absDiffX > absDiffY ? absDiffX : absDiffY
							) / 10
						);
					i < t;
					i++
				){
					// 绘制
					canvasMask.draw(
						ellipse,
						ceil(lastX + diffX / t * i),
						ceil(lastY + diffY / t * i)
					);
				}

				lastX = x;
				lastY = y;
				current -= absDiffX + absDiffY;

				if(
					current > 0 === false
				){
					canvasMask.current = 0;

					canvasList.dispatch(clearEvent);
				}
				else {
					canvasMask.current = current;
				}

				e.preventDefault();
			}
		});

		this.restore();
	};
	CanvasMask = new Class(CanvasMask, "jQun.CanvasMask", Canvas.prototype);

	CanvasMask.override({
		restore : function(){
			///	<summary>
			///	恢复遮罩。
			///	</summary>
			var context = this.context;

			this.clear();

			context.globalCompositeOperation = "source-over";

			if(
				this.image
			){
				this.draw(this.image, 0, 0, this.width, this.height);
			}
			else {
				context.fillStyle = "rgba(255, 255, 255, 0.8)";
				context.fillRect(0, 0, this.width, this.height);
			}

			context.globalCompositeOperation = "destination-out";
			this.current = this.max;

			return this;
		}
	});

	CanvasMask.props({
		current : 0,
		image : null,
		max : 5500
	});

	return CanvasMask.constructor;
}(
	this.Canvas,
	this.CanvasEllipse,
	jQun.HTMLElementList,
	// SIZE
	30,
	// clearEvent
	new Event("clear"),
	Math.abs,
	Math.ceil
));

defineProperties(jQun, this);
}(
	jQun,
	jQun.Class,
	jQun.EventTargetList,
	jQun.Event,
	jQun.DOM.getCSSNumberValue,
	jQun.toArray,
	jQun.defineProperties
);